// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.
	
module train_comma_lock_sm
#(
parameter           CONSECUTIVE_K28_5_LOCK        =   5                //Define how many K28.5 comma should be consecutive detected
)
(
input               clock,
input               reset_n,

input        [9:0]  lvds_rx_data,
input               train_comma_sm_en,

output  reg         bitslip,
output  reg         train_comma_locked

);

//////////////////////////////////////////////////////////////////////////////////
// States for FSM
//////////////////////////////////////////////////////////////////////////////////
localparam    ST_INIT                         = 4'd0;
localparam    ST_K28_5_HUNTING                = 4'd1;
localparam    ST_BITSLIP                      = 4'd2;
localparam    ST_WAIT                         = 4'd3;
localparam    ST_K28_5_DETECTED               = 4'd4;
localparam    ST_K28_5_LOCKED                 = 4'd5;
	
	
//////////////////////////////////////////////////////////////////////////////////
// Parameters
//////////////////////////////////////////////////////////////////////////////////
localparam    LOW                             = 1'b0;
localparam    HIGH                            = 1'b1;

localparam    TRAIN_COMMA_P                   = 10'b10_1000_0011;
localparam    TRAIN_COMMA_N                   = 10'b01_0111_1100;

localparam    TOTAL_BITS                      = clog2(CONSECUTIVE_K28_5_LOCK);

//////////////////////////////////////////////////////////////////////////////////
// Internal Signals
//////////////////////////////////////////////////////////////////////////////////
reg	[3:0]	             rstate;	
 
reg   [4:0]              wait_bitslip_cnt;
reg   [(TOTAL_BITS-1):0] k28_5_detect_cnt;

//////////////////////////////////////////////////////////////////////////////////
// Combiantional Logic
//////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////
// Sequencial logic
//////////////////////////////////////////////////////////////////////////////////	

//train_comma_lock_sm
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   bitslip                         <=   LOW;
		train_comma_locked              <=   LOW;
		
	   rstate                          <=   ST_INIT;		
	end
	
	else begin
	   case(rstate)
		   ST_INIT: begin
			   bitslip                   <=   LOW;
		      train_comma_locked        <=   LOW;
				
			   if(train_comma_sm_en && lvds_rx_data != 10'd0) begin
				   rstate                 <=   ST_K28_5_HUNTING;
				end
			end //end case of ST_INIT
			
			ST_K28_5_HUNTING: begin
			   bitslip                   <=   LOW;
		      train_comma_locked        <=   LOW;
				
				if(lvds_rx_data != TRAIN_COMMA_P && lvds_rx_data != TRAIN_COMMA_N) begin
				   rstate                 <=   ST_BITSLIP;                
				end
				else begin
				   rstate                 <=   ST_K28_5_DETECTED;
				end
			end //end case of ST_K28_5_HUNTING
			
			ST_BITSLIP: begin
			   bitslip                   <=   HIGH;
				train_comma_locked        <=   LOW;
				
				rstate                    <=   ST_WAIT;
			end //end case of ST_BITSLIP
			
			ST_WAIT: begin
			   bitslip                   <=   LOW;
		      train_comma_locked        <=   LOW;
				
			   if(wait_bitslip_cnt == 4'd9) begin
				   rstate                 <=   ST_K28_5_HUNTING;
				end
			end //end case of ST_WAIT
			
			ST_K28_5_DETECTED: begin
			   bitslip                   <=   LOW;
		      train_comma_locked        <=   LOW;
				
				if(lvds_rx_data != TRAIN_COMMA_P && lvds_rx_data != TRAIN_COMMA_N) begin
				   rstate                 <=   ST_K28_5_HUNTING;
				end
				else if(k28_5_detect_cnt == (CONSECUTIVE_K28_5_LOCK-1)) begin
				   rstate                 <=   ST_K28_5_LOCKED;
				end
			end //end case of ST_K28_5_DETECTED
			
			ST_K28_5_LOCKED: begin
			   bitslip                   <=   LOW;
		      train_comma_locked        <=   HIGH;
				
				if(!train_comma_sm_en) begin
				   rstate                 <=   ST_INIT;
				end
			end //end case of ST_K28_5_LOCKED
			
			default: begin
			   rstate                    <=   ST_INIT;
			end //end case of default
		endcase
	end
end


//10 times bitslip waiting time counter
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   wait_bitslip_cnt                <=   4'd0;
	end
	
	else begin
	   if(rstate != ST_WAIT) begin
		   wait_bitslip_cnt             <=   4'd0;
		end
		else begin
		   wait_bitslip_cnt             <=   wait_bitslip_cnt + 1'b1;
		end
	end
end

//CONSECUTIVE_K28_5_LOCK times consecutive K28.5 lock counter
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   k28_5_detect_cnt                <=   {TOTAL_BITS{1'b0}};
	end
	
	else begin
	   if(rstate == ST_K28_5_DETECTED && (lvds_rx_data == TRAIN_COMMA_P || lvds_rx_data == TRAIN_COMMA_N)) begin
		   k28_5_detect_cnt             <=   k28_5_detect_cnt + 1'b1;
		end
		else begin
		   k28_5_detect_cnt             <=   {TOTAL_BITS{1'b0}};
		end
	end
end


//base 2 logarithm, run in precompile stage
function integer clog2;
input integer value;
begin
   value = (value > 1) ? value-1 : 1;            //this avoids clog returning 0 
   for (clog2=0; value>0; clog2=clog2+1)
     value = value>>1;
end

endfunction
	
endmodule
